﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Linq.Expressions;
using System.Text;
using System.Web.Routing;
using System.Runtime.InteropServices;

namespace GroceryPriceBook.Infrastructure
{
	public static class HtmlHelpers
	{
		/// Editing collections in client dynamically (CRUD work)
		/// https://github.com/ivanz/ASP.NET-MVC-Collection-Editing
		/// http://ivanz.com/2011/06/20/editing-variable-length-reorderable-collections-in-asp-net-mvc-part-2/
		#region Collection Editing
		/// <summary>
		/// Begins a collection item by inserting either a previously used .Index hidden field value for it or a new one.
		/// </summary>
		/// <typeparam name="TModel">Model Object Type</typeparam>
		/// <param name="html"></param>
		/// <param name="collectionName">The name of the collection property from the Model that owns this item such as #itemTable</param>
		/// <returns>a CollectionItemNamePrefixScope object - private to this class</returns>
		public static IDisposable BeginCollectionItem<TModel>(this HtmlHelper<TModel> html, string collectionName)
		{
			// if the collection identifier isn't provided throw exception
			if (String.IsNullOrEmpty(collectionName))
				throw new ArgumentException("collectionName is null or empty.", "collectionName");

			// prefix the collection name with ".Index"
			var collectionIndexFieldName = String.Format("{0}.Index", collectionName);

			// check if the ViewData dictionary contains the collection key yet. if yes then set index to {{:index}} else use index value
			var itemIndex = html.ViewData.ContainsKey(JQueryTemplatingEnabledKey) ? "{{:index}}" : GetCollectionItemIndex(collectionIndexFieldName);

			// create index name like "collectionNameValue[10]"
			var collectionItemName = String.Format("{0}[{1}]", collectionName, itemIndex);

			// create new index field as a hidden input for the templated view being generated
			var indexField = new TagBuilder("input");
			indexField.MergeAttributes(new Dictionary<string, string>() {
				{ "name", collectionIndexFieldName },
				{ "value", itemIndex },
				{ "type", "hidden" },
				{ "autocomplete", "off" }
			});

			// write the new hidden index field to the ViewContext
			html.ViewContext.Writer.WriteLine(indexField.ToString(TagRenderMode.SelfClosing));
			return new CollectionItemNamePrefixScope(html.ViewData.TemplateInfo, collectionItemName);
		}

		private const string JQueryTemplatingEnabledKey = "__BeginCollectionItem_jQuery";

		/// <summary>
		/// Create MVC Partial View containing jsRender markup for a given view model object
		/// </summary>
		/// <typeparam name="TModel">View model object type</typeparam>
		/// <typeparam name="TCollectionItem">type of collection</typeparam>
		/// <param name="html">HtmlHelper reference</param>
		/// <param name="partialViewName">name of the Partial View</param>
		/// <param name="modelDefaultValues">default values provided through instance of the class used in the partial view</param>
		/// <param name="pair1">optional key/value to pass into the partial view's View Data object</param>
		/// <param name="pair2">optional key/value to pass into the partial view's View Data object</param>
		/// <returns>HTML of the partial view</returns>
		public static MvcHtmlString CollectionItemJQueryTemplate<TModel, TCollectionItem>(this HtmlHelper<TModel> html, string partialViewName, TCollectionItem modelDefaultValues, KeyValuePair<string, object>? pair1, KeyValuePair<string, object>? pair2)
		{
			var viewData = new ViewDataDictionary<TCollectionItem>(modelDefaultValues) { { JQueryTemplatingEnabledKey, true } };

			if (pair1 != null)
			{
				viewData.Add(new KeyValuePair<string, object>(pair1.Value.Key, pair1.Value.Value));
			}
			if (pair2 != null)
			{
				viewData.Add(new KeyValuePair<string, object>(pair2.Value.Key, pair2.Value.Value));
			}

			return html.Partial(partialViewName, modelDefaultValues, viewData);
		}

		/// <summary>
		/// Tries to reuse old .Index values from the HttpRequest in order to keep the ModelState consistent
		/// across requests. If none are left returns a new one.
		/// </summary>
		/// <param name="collectionIndexFieldName"></param>
		/// <returns>a GUID string</returns>
		private static string GetCollectionItemIndex(string collectionIndexFieldName)
		{
			// check for previous index for this partial view
			var previousIndices = (Queue<string>)HttpContext.Current.Items[collectionIndexFieldName];

			// if any are found in the view then grab the last index value - otherwise create a new unique index value
			if (previousIndices != null)
				return previousIndices.Count > 0 ? previousIndices.Dequeue() : Guid.NewGuid().ToString();

			// create a new queue of the index values
			HttpContext.Current.Items[collectionIndexFieldName] = previousIndices = new Queue<string>();

			var previousIndicesValues = HttpContext.Current.Request[collectionIndexFieldName];

			// if any are found in the view then grab the last index value - otherwise create a new unique index value
			if (String.IsNullOrWhiteSpace(previousIndicesValues))
				return previousIndices.Count > 0 ? previousIndices.Dequeue() : Guid.NewGuid().ToString();

			foreach (var index in previousIndicesValues.Split(','))
				previousIndices.Enqueue(index);

			// return last index value or create a new one if there are none - this is a GUID value
			return previousIndices.Count > 0 ? previousIndices.Dequeue() : Guid.NewGuid().ToString();
		}

		/// <summary>
		/// 
		/// </summary>
		private class CollectionItemNamePrefixScope : IDisposable
		{
			/// <summary>
			/// 
			/// </summary>
			private readonly TemplateInfo _templateInfo;

			/// <summary>
			/// 
			/// </summary>
			private readonly string _previousPrefix;

			/// <summary>
			/// 
			/// </summary>
			/// <param name="templateInfo"></param>
			/// <param name="collectionItemName"></param>
			public CollectionItemNamePrefixScope(TemplateInfo templateInfo, string collectionItemName)
			{
				this._templateInfo = templateInfo;

				_previousPrefix = templateInfo.HtmlFieldPrefix;
				templateInfo.HtmlFieldPrefix = collectionItemName;
			}

			/// <summary>
			/// 
			/// </summary>
			public void Dispose()
			{
				_templateInfo.HtmlFieldPrefix = _previousPrefix;
			}
		}
		#endregion
	}
}